import { ref, watch, computed, nextTick, getCurrentInstance } from 'vue';
import type { Ref } from 'vue';
import { sleep, getRect, getNvueRect } from '../../../libs/utils';
import type { rectSize } from '../../../libs/utils';
import { getPx } from '../../../libs/utils/_utils';
import { delay } from '../utils';
import type { hooksParam, CommonState } from '.';
import { Enum } from '../env';

// #ifdef APP-NVUE
const weexDom = weex.requireModule('dom');
// #endif

const useScroll = (
	ctx: ReturnType<typeof getCurrentInstance>,
	option: Ref<hooksParam>,
	emit: ReturnType<typeof defineEmits>,
	commonState: CommonState,
	isChatRecordModeAndInversion: Ref<boolean>,
	toViewCb: () => {
		finalUseVirtualList: boolean;
		isCellFixed: boolean;
		virtualCellHeight: number;
		virtualHeightCacheList: {
			height: number;
			lastTotalHeight: number;
			totalHeight: number;
		}[];
	},
	getVirtualPlaceholderTopHeight:() => number
) => {
	const scrollTop = ref(0);
	const oldScrollTop = ref(0);

	watch(oldScrollTop, (v) => {
		!option.value.usePageScroll && scrollTopChange(v, false);
	});

	const scrollViewStyle = ref<Record<string, any>>({});
	const scrollViewInStyle = ref<Record<string, any>>({});
	const scrollViewElRef = ref();

	watch(
		() => option.value.usePageScroll,
		(v) => {
			option.value.autoHeight && setAutoHeight(!v);
			// #ifdef H5
			if (v) {
				nextTick(() => {
					console.log(scrollViewElRef.value);
					// const mainScrollRef = this.$refs['e-scroll-view__super-container__scroll-view'].$refs.main;
					// if (mainScrollRef) {
					// 	mainScrollRef.style = {};
					// }
				});
			}
			// #endif
		},
		{
			immediate: true
		}
	);

	const finalScrollViewStyle = computed(() => {
		if (option.value.superContentZIndex != 1) {
			scrollViewStyle.value['z-index'] = option.value.superContentZIndex;
			scrollViewStyle.value['position'] = 'relative';
		}
		return scrollViewStyle.value;
	});
	// 设置z-paging高度
	const setAutoHeight = async (shouldFullHeight = true, scrollViewNode?: rectSize) => {
		let heightKey = 'min-height';
		// #ifndef APP-NVUE
		heightKey = 'min-height';
		// #endif
		try {
			if (shouldFullHeight) {
				// 如果需要铺满全屏，则计算当前全屏可是区域的高度
				let finalScrollViewNode = scrollViewNode;
				if (!finalScrollViewNode) {
					// #ifndef APP-NVUE
					finalScrollViewNode = await getRect(ctx, '.e-scroll-view__super-container__scroll-view');
					// #endif
				}

				if (finalScrollViewNode) {
					const scrollViewTop = finalScrollViewNode.top!;
					let scrollViewHeight = (commonState.systemInfo.windowHeight || 0) - scrollViewTop;

					const additionHeight = getPx(option.value.autoHeightAddition);
					const finalHeight = scrollViewHeight + additionHeight + 'px !important';
					scrollViewStyle.value[heightKey] = finalHeight;
					scrollViewInStyle.value[heightKey] = finalHeight;
				}
			} else {
				scrollViewStyle.value[heightKey] = undefined;
				scrollViewInStyle.value[heightKey] = undefined;
			}
		} catch (e) {}
	};

	const pageScrollTop = ref(-1);
	watch(pageScrollTop, (v) => {
		option.value.usePageScroll && scrollTopChange(v, true);
	});

	const privateScrollWithAnimation = ref(-1);
	const finalScrollWithAnimation = computed(() => {
		if (privateScrollWithAnimation.value !== -1) {
			return privateScrollWithAnimation.value === 1;
		}
		return option.value?.scrollWithAnimation;
	});

	const finalScrollTop = computed(() => {
		return option.value?.usePageScroll ? pageScrollTop.value : oldScrollTop.value;
	});

	// 当前scroll-view/list-view是否允许滚动
	const finalScrollable = computed(() => {
		return (
			option.value?.scrollable &&
			!option.value?.usePageScroll &&
			commonState.scrollEnable &&
			(option.value?.refresherCompleteScrollable ? true : commonState.refresherStatus !== Enum.Refresher.Complete) &&
			(option.value?.refresherRefreshingScrollable ? true : commonState.refresherStatus !== Enum.Refresher.Loading)
		);
	});

	// 滚动到顶部，animate为是否展示滚动动画，默认为是
	const scrollToTop = (animate: boolean | undefined, checkReverse = true) => {
		// 如果是聊天记录模式并且列表倒置了，则滚动到顶部实际上是滚动到底部
		if (option.value?.useChatRecordMode && checkReverse) {
			scrollToBottom(animate, false);
			return;
		}
		nextTick(async () => {
			_scrollToTop(animate);

			// #ifdef APP-NVUE
			if (option.value?.nvueFastScroll && animate) {
				await sleep(100);
				_scrollToTop(false);
			}
			// #endif
		});
	};
	// 滚动到底部，animate为是否展示滚动动画，默认为是
	const scrollToBottom = (animate?: boolean, checkReverse = true) => {
		// 如果是聊天记录模式并且列表倒置了，则滚动到底部实际上是滚动到顶部
		if (option.value?.useChatRecordMode && checkReverse) {
			scrollToTop(animate, false);
			return;
		}
		nextTick(async () => {
			_scrollToBottom(animate);
			// #ifdef APP-NVUE
			if (option.value?.nvueFastScroll && animate) {
				await sleep(100);
				_scrollToBottom(false);
			}
			// #endif
		});
	};
	// 滚动到指定view(vue中有效)。sel为需要滚动的view的id值，不包含"#"；offset为偏移量，单位为px；animate为是否展示滚动动画，默认为否
	const scrollIntoViewById = (sel: string, offset?: number, animate?: boolean) => {
		_scrollIntoView(sel, offset, animate);
	};
	// 滚动到指定view(vue中有效)。nodeTop为需要滚动的view的top值(通过uni.createSelectorQuery()获取)；offset为偏移量，单位为px；animate为是否展示滚动动画，默认为否
	const scrollIntoViewByNodeTop = (nodeTop: number, offset: number, animate: boolean) => {
		scrollTop.value = oldScrollTop.value;
		nextTick(() => {
			_scrollIntoViewByNodeTop(nodeTop, offset, animate);
		});
	};
	// 滚动到指定位置(vue中有效)。y为与顶部的距离，单位为px；offset为偏移量，单位为px；animate为是否展示滚动动画，默认为否
	const scrollToY = (y: number, offset?: number, animate?: boolean) => {
		scrollTop.value = oldScrollTop.value;
		nextTick(() => {
			_scrollToY(y, offset, animate);
		});
	};
	// 滚动到指定view(nvue中有效)。index为需要滚动的view的index(第几个，从0开始)；offset为偏移量，单位为px；animate为是否展示滚动动画，默认为否
	const scrollIntoViewByIndex = (index: number, offset?: number, animate?: boolean) => {
		nextTick(() => {
			// #ifdef APP-NVUE
			// 在nvue中，根据index获取对应节点信息并滚动到此节点位置
			_scrollIntoView(index, offset, animate);
			// #endif
			// #ifndef APP-NVUE
			const { finalUseVirtualList, isCellFixed, virtualCellHeight, virtualHeightCacheList } = toViewCb();
			if (finalUseVirtualList) {
				delay(
					() => {
						if (finalUseVirtualList) {
							// 虚拟列表 + 每个cell高度完全相同模式下，此时滚动到对应index的cell就是滚动到scrollTop = cellHeight * index的位置
							// 虚拟列表 + 高度是动态非固定的模式下，此时滚动到对应index的cell就是滚动到scrollTop = 缓存的cell高度数组中第index个的lastTotalHeight的位置
							const scrollTop = isCellFixed ? virtualCellHeight * index : virtualHeightCacheList[index].lastTotalHeight;
							scrollToY(scrollTop, offset, animate);
						}
					},
					isCellFixed ? 0 : 100
				);
			}
			// #endif
		});
	};
	// 滚动到指定view(nvue中有效)。view为需要滚动的view(通过`this.$refs.xxx`获取)，不包含"#"；offset为偏移量，单位为px；animate为是否展示滚动动画，默认为否
	const scrollIntoViewByView = (view: string, offset?: number, animate?: boolean) => {
		_scrollIntoView(view, offset, animate);
	};
	// 当使用页面滚动并且自定义下拉刷新时，请在页面的onPageScroll中调用此方法，告知z-paging当前的pageScrollTop，否则会导致在任意位置都可以下拉刷新
	const updatePageScrollTop = (value: number) => {
		pageScrollTop.value = value;
	};

	// 更新z-paging内置scroll-view的scrollTop
	const updateScrollViewScrollTop = (_scrollTop: number, animate = true) => {
		updatePrivateScrollWithAnimation(animate);
		scrollTop.value = oldScrollTop.value;
		nextTick(() => {
			scrollTop.value = _scrollTop;
			oldScrollTop.value = scrollTop.value;
		});
	};

	// 在nvue中处于顶部的view，用于回到顶部时定位
	const listTopAagElRef = ref();
	// nvue的refresh组件
	const refreshElRef = ref();
	// 在nvue中处于底部的view，用于回到底部时定位
	const listBottomAagElRef = ref();

	// 当滚动到顶部时
	const _onScrollToUpper = () => {
		emit('scrolltoupper');
		emit('scrollTopChange', 0);
		nextTick(() => {
			oldScrollTop.value = 0;
		});
	};
	// 当滚动到底部时
	const _onScrollToLower = (e: { detail: { direction: string } }) => {
		(!e.detail || !e.detail.direction || e.detail.direction === 'bottom') && emit('scrolltolower', option.value.useChatRecordMode ? 'click' : 'toBottom');
	};
	// 滚动到顶部
	const _scrollToTop = (animate = true) => {
		// #ifdef APP-NVUE
		// 在nvue中需要通过weex.scrollToElement滚动到顶部，此时在顶部插入了一个view，使得滚动到这个view位置

		if (option.value?.usePageScroll) {
			weexDom.scrollToElement(listTopAagElRef.value, {
				offset: 0,
				animated: animate
			});
		} else {
			if (!commonState.isIos && option.value.nvueListIs === 'scroller') {
				getNvueRect(refreshElRef.value).then((size) => {
					const nodeHeight = size.height || 0;
					weexDom.scrollToElement(listTopAagElRef.value, {
						offset: -nodeHeight,
						animated: animate
					});
				});
			} else {
				weexDom.scrollToElement(listTopAagElRef.value, {
					offset: 0,
					animated: animate
				});
			}
		}

		// #endif
		if (option.value.usePageScroll) {
			nextTick(() => {
				uni.pageScrollTo({
					scrollTop: 0,
					duration: animate ? 100 : 0
				});
			});
			return;
		}
		updatePrivateScrollWithAnimation(animate);
		scrollTop.value = oldScrollTop.value;
		nextTick(() => {
			scrollTop.value = 0;
			oldScrollTop.value = scrollTop.value;
		});
	};
	// 滚动到底部
	const _scrollToBottom = async (animate = true) => {
		// #ifdef APP-NVUE
		// 在nvue中需要通过weex.scrollToElement滚动到顶部，此时在底部插入了一个view，使得滚动到这个view位置
		if (listBottomAagElRef.value) {
			weexDom.scrollToElement(listBottomAagElRef.value, {
				offset: 0,
				animated: animate
			});
		}

		// #endif
		// #ifndef APP-NVUE
		if (option.value.usePageScroll) {
			nextTick(() => {
				uni.pageScrollTo({
					scrollTop: Number.MAX_VALUE,
					duration: animate ? 100 : 0
				});
			});
			return;
		}
		try {
			updatePrivateScrollWithAnimation(animate);
			const pagingContainerNode = await getRect(ctx, '.e-scroll-view__container');
			const scrollViewNode = await getRect(ctx, '.e-scroll-view__super-container__scroll-view');
			const pagingContainerH = pagingContainerNode.height || 0;
			const scrollViewH = scrollViewNode.height || 0;
			if (pagingContainerH > scrollViewH) {
				scrollTop.value = oldScrollTop.value;
				nextTick(() => {
					scrollTop.value = pagingContainerH - scrollViewH + getVirtualPlaceholderTopHeight();
					oldScrollTop.value = scrollTop.value;
				});
			}
		} catch (e) {}
		// #endif
	};
	// 滚动到指定view
	const _scrollIntoView = async (sel: string | number, offset = 0, animate = false) => {
		try {
			scrollTop.value = oldScrollTop.value;
			await nextTick();

			// #ifdef APP-NVUE
			const refs = ctx?.parent?.refs;
			if (!refs) return;
			const dataType = Object.prototype.toString.call(sel);
			let el = null;
			if (dataType === '[object Number]') {
				const els: any = refs[`z-paging-${sel}`];
				el = els ? els[0] : null;
			} else if (dataType === '[object Array]') {
				el = (sel as string)[0];
			} else {
				el = sel;
			}
			if (el) {
				weexDom.scrollToElement(el, {
					offset: -offset,
					animated: animate
				});
			} else {
				// u.consoleErr('在nvue中滚动到指定位置，cell必须设置 :ref="`z-paging-${index}`"');
			}

			// #endif
			// #ifndef APP-NVUE
			getRect(ctx.parent, '#' + (sel as string).replace('#', '')).then((size) => {
				if (size) {
					let nodeTop = size.top!;
					_scrollIntoViewByNodeTop(nodeTop, offset, animate);
				}
			});
			// #endif
		} catch (e) {}
	};
	// 通过nodeTop滚动到指定view
	const _scrollIntoViewByNodeTop = (nodeTop: number, offset = 0, animate = false) => {
		// 如果是聊天记录模式并且列表倒置了，此时nodeTop需要等于scroll-view高度 - nodeTop
		if (isChatRecordModeAndInversion.value) {
			getRect(ctx, '.e-scroll-view__super-container__scroll-view').then((size) => {
				if (size) {
					_scrollToY(size.height! - nodeTop, offset, animate, true);
				}
			});
		} else {
			_scrollToY(nodeTop, offset, animate, true);
		}
	};
	// 滚动到指定位置
	const _scrollToY = (y: number, offset = 0, animate = false, addScrollTop = false) => {
		updatePrivateScrollWithAnimation(animate);
		sleep(10).then(() => {
			if (option.value.usePageScroll) {
				if (addScrollTop && pageScrollTop.value !== -1) {
					y += pageScrollTop.value;
				}
				const _scrollTop = y - offset;
				uni.pageScrollTo({
					scrollTop: _scrollTop,
					duration: animate ? 100 : 0
				});
			} else {
				if (addScrollTop) {
					y += oldScrollTop.value;
				}
				scrollTop.value = y - offset;
			}
		});
	};
	// 最终的底部加载更多触发阈值
	const finalLowerThreshold = computed(() => {
		return getPx(option.value.lowerThreshold);
	});
	let cacheScrollNodeHeight = -1;
	// 通过@scroll事件检测是否滚动到了底部(顺带检测下是否滚动到了顶部)
	const checkScrolledToBottom = (scrollDiff: number, checked = false) => {
		// 如果当前scroll-view高度未获取，则获取其高度
		if (cacheScrollNodeHeight === -1) {
			// 获取当前scroll-view高度
			getRect(ctx, '.e-scroll-view__super-container__scroll-view').then((size) => {
				if (size.height) {
					const scrollNodeHeight = size.height;
					// 缓存当前scroll-view高度，如果获取过了不再获取
					cacheScrollNodeHeight = scrollNodeHeight;
					// // scrollDiff - cacheScrollNodeHeight = 当前滚动区域的顶部与内容底部的距离 - scroll-view高度 = 当前滚动区域的底部与内容底部的距离(也就是最终的与底部的距离)
					if (scrollDiff - scrollNodeHeight <= finalLowerThreshold.value) {
						// 如果与底部的距离小于阈值，则判断为滚动到了底部，触发滚动到底部事件
						emit('scrolltolower', 'toBottom');
					}
				}
			});
		} else {
			// scrollDiff - cacheScrollNodeHeight = 当前滚动区域的顶部与内容底部的距离 - scroll-view高度 = 当前滚动区域的底部与内容底部的距离(也就是最终的与底部的距离)
			if (scrollDiff - cacheScrollNodeHeight <= finalLowerThreshold.value) {
				// 如果与底部的距离小于阈值，则判断为滚动到了底部，触发滚动到底部事件
				emit('scrolltolower', 'toBottom');
			} else if (scrollDiff - cacheScrollNodeHeight <= 500 && !checked) {
				// 如果与底部的距离小于500px，则获取当前滚动的位置，延迟150毫秒重复上述步骤再次检测(避免@scroll触发时获取的scrollTop不正确导致的其他问题，此时获取的scrollTop不一定可信)。防止因为部分性能较差安卓设备@scroll采样率过低导致的滚动到底部但是依然没有触发的问题
				delay(
					() => {
						getRect(ctx, '.e-scroll-view__super-container__scroll-view').then((size) => {
							if (size) {
								oldScrollTop.value = size.scrollTop!;
								const newScrollDiff = size.scrollHeight! - oldScrollTop.value;
								checkScrolledToBottom(newScrollDiff, true);
							}
						});
					},
					150,
					'checkScrolledToBottomDelay'
				);
			}
			// 检测一下是否已经滚动到了顶部了，因为在安卓中滚动到顶部时scrollTop不一定为0(和滚动到底部一样的原因)，所以需要在scrollTop小于150px时，通过获取.e-scroll-view__super-container__scroll-view的scrollTop再判断一下
			if (oldScrollTop.value <= 150 && oldScrollTop.value !== 0) {
				delay(
					() => {
						// 这里再判断一下是否确实已经滚动到顶部了，如果已经滚动到顶部了，则不用再判断了，再次判断的原因是可能150毫秒之后oldScrollTop才是0
						if (oldScrollTop.value !== 0) {
							getRect(ctx, '.e-scroll-view__super-container__scroll-view').then((size) => {
								// 如果150毫秒后.e-scroll-view__super-container__scroll-view的scrollTop为0，则认为已经滚动到了顶部了
								if (size?.scrollTop === 0 && oldScrollTop.value !== 0) {
									_onScrollToUpper();
								}
							});
						}
					},
					150,
					'checkScrolledToTopDelay'
				);
			}
		}
	};
	// scroll-view滚动中
	const handleVueScroll = (e: { detail: { scrollTop: number; scrollHeight: number } }) => {
		emit('scroll', e);
		const _scrollTop = e.detail.scrollTop;

		oldScrollTop.value = _scrollTop;
		// 滚动区域内容的总高度 - 当前滚动的scrollTop = 当前滚动区域的顶部与内容底部的距离
		const scrollDiff = e.detail.scrollHeight - oldScrollTop.value;
		// 在非ios平台滚动中，再次验证一下是否滚动到了底部。因为在一些安卓设备中，有概率滚动到底部不触发@scrolltolower事件，因此添加双重检测逻辑
		!commonState.isIos && checkScrolledToBottom(scrollDiff);
	};
	// 更新内置的scroll-view是否启用滚动动画
	const updatePrivateScrollWithAnimation = (animate?: boolean) => {
		privateScrollWithAnimation.value = animate ? 1 : 0;
		delay(
			() =>
				nextTick(() => {
					// 在滚动结束后将滚动动画状态设置回初始状态
					privateScrollWithAnimation.value = -1;
				}),
			100,
			'updateScrollWithAnimationDelay'
		);
	};

	const pagingContentElRef = ref();

	// 更新缓存中z-paging整个内容容器高度
	const updateCachedSuperContentHeight = async () => {
		let superContentNode: rectSize;
		// #ifndef APP-NVUE
		superContentNode = await getRect(ctx, '.e-scroll-view');
		// #endif
		// #ifdef APP-NVUE
		superContentNode = await getNvueRect(pagingContentElRef.value);
		// #endif

		if (superContentNode.height) {
			commonState.superContentHeight = superContentNode.height;
		}
	};

	const wxsPageScrollTop = ref(0);
	// scrollTop改变时触发
	const scrollTopChange = (newVal: number, isPageScrollTop?: boolean) => {
		emit('scrollTopChange', newVal);
		emit('update:scrollTop', newVal);
		// 之前在安卓中scroll-view有概率滚动到顶部时scrollTop不为0导致下拉刷新判断异常，因此判断scrollTop在105之内都允许下拉刷新，但此方案会导致某些情况（例如滚动到距离顶部10px处）下拉抖动，因此改为通过获取e-scroll-view__super-container__scroll-view的节点信息中的scrollTop进行验证的方案
		// const scrollTop = this.isIos ? (newVal > 5 ? 6 : 0) : (newVal > 105 ? 106 : (newVal > 5 ? 6 : 0));
		const scrollTop = newVal > 5 ? 6 : 0;
		if (isPageScrollTop && wxsPageScrollTop.value !== scrollTop) {
			wxsPageScrollTop.value = scrollTop;
		} else if (!isPageScrollTop && commonState.wxsScrollTop !== scrollTop) {
			commonState.wxsScrollTop = scrollTop;
			if (scrollTop > 6) {
				commonState.scrollEnable = true;
			}
		}
	};

	return {
		scrollTop,
		oldScrollTop,
		scrollViewStyle,
		scrollViewInStyle,
		scrollViewElRef,
		finalScrollViewStyle,
		setAutoHeight,
		pageScrollTop,
		privateScrollWithAnimation,
		finalScrollWithAnimation,
		finalScrollTop,
		finalScrollable,
		scrollToTop,
		scrollToBottom,
		scrollIntoViewById,
		scrollIntoViewByNodeTop,
		scrollToY,
		scrollIntoViewByIndex,
		scrollIntoViewByView,
		updatePageScrollTop,
		updateScrollViewScrollTop,
		listTopAagElRef,
		refreshElRef,
		listBottomAagElRef,
		_onScrollToUpper,
		_onScrollToLower,
		_scrollIntoView,
		_scrollIntoViewByNodeTop,
		_scrollToY,
		finalLowerThreshold,
		checkScrolledToBottom,
		handleVueScroll,
		updatePrivateScrollWithAnimation,
		pagingContentElRef,
		updateCachedSuperContentHeight,
		wxsPageScrollTop,
		scrollTopChange
	};
};

export default useScroll;
